/**
 * @file screen_manager.cpp
 * @author LinTeX9527 (lintex9527@yeah.net)
 * @brief 屏幕显示管理，菜单项管理，各个界面的跳转，都在此显示
 * @version 0.1
 * @date 2024-11-20
 *
 * @copyright Copyright (c) 2024
 *
 */


#include <Arduino.h>
#include <Adafruit_ST7789.h>
#include <Fonts/FreeSans12pt7b.h>
#include <lvgl.h>
#include "lv_port_indev.h"
#include "ui_task.h"
#include "btn_drv.h"
#include "dlight_bh1750.h"
#include "lv_examples.h"

#define GUI_APP_LIGHT_METER_ENABLE         1


#if GUI_APP_LIGHT_METER_ENABLE
#include "gui_guider.h"
#include "custom.h"
#include "events_init.h"
#include "widgets_init.h"

lv_ui guider_ui;
#endif


#define LCD_FB_BYTE_PER_PIXEL     2
/* The virtual buffer for DBI panel, it should be ~1/10 screen size. */
#define LCD_VIRTUAL_BUF_HEIGHT    (LCD_HEIGHT / 10)
#define LCD_VIRTUAL_BUF_SIZE      (LCD_WIDTH * LCD_VIRTUAL_BUF_HEIGHT)

/* 显示屏，硬件对象 */
Adafruit_ST7789 mDisplay = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

static uint8_t s_frameBuffer[2][LCD_WIDTH * LCD_HEIGHT / 10 * LCD_FB_BYTE_PER_PIXEL]; // 最后 x2 是因为一个像素点占据2个字节

/* Flush the content of the internal buffer the specific area on the display
 * You can use DMA or any hardware acceleration to do this operation in the background but
 * 'lv_flush_ready()' has to be called when finished
 * This function is required only when LV_VDB_SIZE != 0 in lv_conf.h*/
static void DEMO_FlushDisplay(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
    lv_coord_t x1 = area->x1;
    lv_coord_t y1 = area->y1;
    lv_coord_t x2 = area->x2;
    lv_coord_t y2 = area->y2;

	mDisplay.drawRGBBitmap((int16_t)x1, (int16_t)y1, (uint16_t *)color_p, (int16_t)(x2 - x1 + 1), (int16_t)(y2 - y1 + 1));

	/* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);
}

void lv_port_disp_init(void)
{
	static lv_disp_draw_buf_t disp_buf;

	memset(s_frameBuffer, 0, sizeof(s_frameBuffer));
	lv_disp_draw_buf_init(&disp_buf, (void *)s_frameBuffer[0], (void *)s_frameBuffer[1], LCD_VIRTUAL_BUF_SIZE);

	static lv_disp_drv_t disp_drv;
	lv_disp_drv_init(&disp_drv);
	disp_drv.hor_res = LCD_WIDTH;
	disp_drv.ver_res = LCD_HEIGHT;

	disp_drv.flush_cb = DEMO_FlushDisplay;

	disp_drv.draw_buf = &disp_buf;
	lv_disp_drv_register(&disp_drv);
}

void dbg_lvgl_print_cb(const char *buf)
{
	Serial.println(buf);
}

/**
 * @brief 显示屏初始化，显示唤醒内容
 *
 * @param
 */
void setup_ui_task(void)
{
	/* turn on the TFT / I2C power supply */
	pinMode(TFT_I2C_POWER, OUTPUT); /* 这个管脚是必须拉高的 */
	digitalWrite(TFT_I2C_POWER, HIGH);

	/* 因为这里旋转了屏幕的方向，所以初始化的屏幕尺寸和实际使用的尺寸颠倒了 */
	mDisplay.init(LCD_HEIGHT, LCD_WIDTH); // Init ST7789 240x135
	mDisplay.setRotation(3);

	pinMode(TFT_BACKLITE, OUTPUT);
	digitalWrite(TFT_BACKLITE, HIGH);

	/* 初始化 LVGL */
	lv_init();
	lv_log_register_print_cb(dbg_lvgl_print_cb);
	lv_port_disp_init();
	lv_port_indev_init();
	lv_task_handler();

#if GUI_APP_LIGHT_METER_ENABLE
	/*Create a GUI-Guider app */
	setup_ui(&guider_ui);
	custom_init(&guider_ui);
	events_init(&guider_ui);
#else
	// 启动示例
	// lv_example_anim_1();
	lv_example_anim_2(); // 一个红色球从左边移动到右边，逐渐变大
	// lv_example_anim_3();
	// lv_example_anim_timeline_1();
	// lv_example_get_started_1();
#endif
}

void loop_ui_task(void)
{
#if GUI_APP_LIGHT_METER_ENABLE
	static int current_time = 0;
	static int previous_time = 0;

	// 每隔300毫秒更新 GUI 显示最新的光照强度数值
	current_time = millis();
	if (current_time - previous_time >= 300) {
		previous_time = current_time;
		if (guider_ui.screen_lightmeter_lbl_lux_value) {
			lv_event_send(guider_ui.screen_lightmeter_lbl_lux_value, LV_EVENT_REFRESH, NULL);
		}
	}
#endif

	// LV_TICK_CUSTOM == 1 就没必要手动调用 lv_tick_inc() 函数
	lv_task_handler();
}
